`include "gpu_bus.sv"
module gpu_bus_slave #(
    parameter DSIZE = 128,
    parameter ASIZE = 20,
    parameter ADDR_WIDTH = 32,
    parameter ID_WIDTH = 4,
    parameter BL_WIDTH = 8
)
(
    input                clk,
    input                rst_n,
    gpu_bus              sink,
    output  [ASIZE-1:0]  ram_addr,
    output               ram_wrn,
    output               ram_csn,
    output  [DSIZE-1:0]  ram_wdata,
    input   [DSIZE-1:0]  ram_rdata
);

wire   cmd_req;
logic  cmd_ack;

wire  [ADDR_WIDTH-1:0]   inter_Gaddr;
wire  [BL_WIDTH-1:0]     inter_Gbl;
wire  [ID_WIDTH-1:0]     inter_Gid;
wire                     inter_Gwrite;
logic                    inter_Grlast;
    
    sink_addr_store #
    (
        .ADDR_WIDTH(ADDR_WIDTH),
        .ID_WIDTH  (ID_WIDTH),
        .BL_WIDTH  (BL_WIDTH),
        .ADDR_DEPTH(2)
    ) u_sink_addr_store
    (
        .clk(clk),
        .rst_n(rst_n),
        .Gaddr  (sink.Gaddr),
        .Gbl    (sink.Gbl),
        .Gid    (sink.Gid),
        .Gwrite (sink.Gwrite),
        .Gvalid (sink.Gvalid),
        .Gready (sink.Gready),
        .SGaddr (inter_Gaddr),
        .SGbl   (inter_Gbl),                    
        .SGid   (inter_Gid),       
        .SGwrite(inter_Gwrite),                      
        .req    (cmd_req),
        .ack    (cmd_ack)
    );  


typedef enum {IDLE,WRITE,READ} state_t;

state_t state,next_state,state_ff;

always_ff @(posedge clk or negedge rst_n)
begin
    if(!rst_n) state <= IDLE;
    else state <= next_state;
end

always_ff @(posedge clk or negedge rst_n)
begin
    if(!rst_n) state_ff <= IDLE;
    else state_ff <= state;
end               

always @*
begin
    case(state)
        IDLE:
            if(cmd_req&&inter_Gwrite) next_state = WRITE;
            else if(cmd_req && !inter_Gwrite) next_state = READ;
            else next_state = IDLE;
        WRITE:
            if(sink.Gwlast) next_state = IDLE;
            //begin
            //    if(cmd_req&&inter_Gwrite) next_state = WRITE;
            //    else if(cmd_req && !inter_Gwrite) next_state = READ;
            //    else next_state = IDLE;                   
            //end
            else next_state = WRITE;
        READ:
            if(inter_Grlast) next_state = IDLE;
            //begin
            //    if(cmd_req&&inter_Gwrite) next_state = WRITE;
            //    else if(cmd_req && !inter_Gwrite) next_state = READ;
            //    else next_state = IDLE;                   
            //end
            else next_state = READ;               
        default: next_state = IDLE;
    endcase
end


assign cmd_ack = ((state == WRITE) && sink.Gwlast) || 
                 ((state == READ) & inter_Grlast);

logic [BL_WIDTH-1:0] wcnt;
logic [BL_WIDTH-1:0] rcnt;

always_ff @(posedge clk or negedge rst_n)
begin
    if(!rst_n) wcnt <= 0;
    else if(sink.Gwlast) wcnt <= 0;
    else if(next_state == WRITE && sink.Gwvalid && sink.Gwready) wcnt <= wcnt + 1'b1;
end

always_ff @(posedge clk or negedge rst_n)
begin
    if(!rst_n) rcnt <= 0;
    else if((state == READ) && sink.Grready)
    begin
        if(rcnt == inter_Gbl ) rcnt <= 0;
        else rcnt <= rcnt + 1'b1;
    end
end             


assign ram_addr = (state == WRITE) ? (inter_Gaddr>>($clog2(DSIZE)-3)) + wcnt :
                  (state == READ ) ? (inter_Gaddr>>($clog2(DSIZE)-3)) + rcnt : 0;

assign ram_csn = (state == IDLE) ? 1'b1 : 1'b0;

assign ram_wrn = (state == WRITE) ? !(sink.Gwvalid && sink.Gwready) : 1'b1;

assign ram_wdata = sink.Gwdata;


//rdata
assign sink.Grdata = (state == READ) ? ram_rdata : 0;
assign sink.Grid = inter_Gid;
assign sink.Grvalid = (state == READ) ? 1 : 0;
assign sink.Grfirst = (state == READ) && (rcnt == 0);
assign sink.Grlast  = (state == READ) && (rcnt == inter_Gbl )&& sink.Grready;
assign inter_Grlast  = (state == READ) && (rcnt == inter_Gbl )&& sink.Grready;

//always_ff @(posedge clk or negedge rst_n)
//begin
//    if(!rst_n) sink.Grfirst <= 0;
//    else sink.Grfirst <= (state == READ) && (rcnt == 0);
//end
//
//always_ff @(posedge clk or negedge rst_n)
//begin
//    if(!rst_n) sink.Grlast <= 0;
//    else sink.Grlast <= inter_Grlast;
//end
//
//always_ff @(posedge clk or negedge rst_n)
//begin
//    if(!rst_n) sink.Grid <= 0;
//    else  sink.Grid <= inter_Gid;
//end

//wdata
assign sink.Gwready = (state == WRITE) ? 1 : 0;

endmodule
